// 使用一个第三方包，为了判断 /scores/:id 格式的动态路由
const { match } = require("path-to-regexp");

// 导出一个函数，并返回一个中间件函数
// 导出函数是为了使用 config.default.js 中，对中间件的配置信息
module.exports = (options) => {
  // 返回一个授权中间件
  return async (ctx, next) => {
    // 如果在白名单中的请求，就直接放行
    if (options.whiteList.includes(ctx.path)) {
      await next();
      // 如果不是白名单中的请求，就需要校验 token
    } else {
      // 获取请求 header 中的 token
      let token = ctx.headers.token;
      // 如果没有 token，就返回 401 表示用户没有登录
      if (!token) {
        ctx.body = {
          code: 401,
          message: "缺少 token 字段",
        };
        return;
      }
      // 校验 token 是不是服务器签发的
      try {
        // 如果是服务器签发的，就会校验成功，并返回 token 中包含的用户信息
        ctx.profile = ctx.helper.checkToken(token);
      } catch (error) {
        // 如果不是服务器签发的，或者是前端修改后的 token，或者是过期的 token，就都会报错
        // 在这里捕获错误，并响应提示前台
        // Token 过期错误提示
        if (error.name === "TokenExpiredError") {
          ctx.body = {
            code: 401,
            message: "token过期",
          };
          return;
        }
        // 无效 Token 错误提示
        if (error.name === "JsonWebTokenError") {
          ctx.body = {
            code: 401,
            message: "无效的token",
          };
          return;
        }
      }
      // 校验接口权限
      let aclList = await ctx.service.user.readAclsByUid(ctx.profile.uid);
      let isAllow = aclList.some((item) => {
        let instance = match(item.path, {
          decode: decodeURIComponent,
        });
        return instance(ctx.path) && ctx.method === item.method;
      });
      if (!isAllow) {
        ctx.body = {
          code: 403,
          message: "你无权请求该接口",
        };
        return;
      }
      // 所有校验都通过后，就继续向下执行
      await next();
    }
  };
};
